/*
    作者：zx
    时间：2022.11.8
    描述：cpu相关数据类型(段描述符、段选择子、门描述符)的定义
*/

#ifndef CPU_H
#define CPU_H

#include "comm/types.h"

// EFLAGS寄存器每一位的意义
// 用以tss结构
#define EFLAGS_ALWAYS_1 (1<<1)  // 这一位默认为1
#define EFLAGS_IF (1<<9) // 打开全局中断,因为切换进程

// 段描述符每一位的意义
#define SEG_G				(1 << 15)		// 设置段界限的单位，1-4KB，0-字节
#define SEG_D				(1 << 14)		// 控制是否是32位、16位的代码或数据段
#define SEG_P_PRESENT	    (1 << 7)		// 段是否存在

#define SEG_DPL0			(0 << 5)		// 访问该段需要的特权级0，最高特权级
#define SEG_DPL3			(3 << 5)		// 访问该段需要的特权级3，最低权限

#define SEG_S_SYSTEM		(0 << 4)		// 是否是系统段，如调用门或者中断
#define SEG_S_NORMAL		(1 << 4)		// 普通的代码段或数据段

#define SEG_TYPE_CODE		(1 << 3)		// 指定其为代码段
#define SEG_TYPE_DATA		(0 << 3)		// 数据段
#define SEG_TYPE_TSS        (9 << 0)        // TSS结构体段

#define SEG_TYPE_RW			(1 << 1)		// 是否可写可读，不设置为只读

#define SEG_CPL3            (3 << 0)        // CPL(当前代码段所属权限级)
#define SEG_CPL0            (0 << 0)

//段描述符
#pragma pack(1)
typedef struct _segment_desc_t {
	uint16_t limit15_0;
	uint16_t base15_0;
	uint8_t base23_16;
	uint16_t attr;
	uint8_t base31_24;
}segment_desc_t;

#define GATE_P_PRESENT (1<<15) 
#define GATE_DPL0 (0<<13)
#define GATE_DPL3 (3<<13)
#define GATE_TYPE_IDT (0xE << 8)
#define GATE_TYPE_SYSCALL (0xc << 8)

// 门描述符
typedef struct _gate_desc_t
{
	uint16_t offset15_0;
	uint16_t selector;
	uint16_t attr;
	uint16_t offset31_16;
}gate_desc_t;

// tss结构表 用以进行进程切换时的保存现场
typedef struct _tss_t
{
    uint32_t previous_task_link; 
    uint32_t esp0,ss0,esp1,ss1,esp2,ss2;
    // cr3 指示页表
    uint32_t cr3,eip,eflags,eax,ecx,edx,ebx,esp,ebp,esi,edi;
    uint32_t es,cs,ss,ds,fs,gs;
    uint32_t ldt_segment_selector;
    uint32_t io_map_base_addr;
    uint32_t ssp; 
}tss_t;


#pragma pack()

void cpu_init(void);
void init_gdt(void);

// 向gdt表selector项，安装base，limit，attr
void segment_desc_set(int selector, uint32_t base, uint32_t limit, uint16_t attr);
// 初始化门描述符desc 安装selector，offset，attr
void gate_desc_set(gate_desc_t * desc, uint16_t selector, uint32_t offset, uint16_t attr);
// 从gdt表中找出空闲项，返回选择子
int gdt_alloc_desc();
int gdt_free_selector(int selector);
void switch_to_tss(int gdt_tss_selector);
#endif // !CPU_H